package com.demo.webapp.servlet;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;

/**
 * 上传的基础Servlet
 * <ul>
 * 	<li>filePath:文件存储路径</li>
 * 	<li>tmpPath:文件存储的临时文件路径</li>
 * 	<li>maxFileSize:上传文件的最大大小</li>
 * 	<li>maxMemSize:上传文件小于内存最大值时，将存储内存中</li>
 * </ul>
 * @author ryuu.kk
 * 
 */
public abstract class UploadServlet extends HttpServlet {

	/**
	 * 版本序列化ID
	 */
	private static final long serialVersionUID = -2950493692114372997L;
	
	//文件最大50M
	protected static final int FILE_SIZE_MAX = 50*1024*1024;
	
	//单文件内存最大使用4M
	protected static final int MEM_SIZE_MAX = 4*1024;
	
	/**
	 * 多部件标志
	 */
	private boolean isMultipart;
	
	/**
	 * 出错跳转地址
	 */
	private String errorUrl;
	
	/**
	 * 目标地址
	 */
	private String target;
	
	/**
	 * 文件保存地址
	 */
	private String filePath;
	
	/**
	 * 临时文件位置 
	 */
	private String tmpPath = "/data";
	
	/**
	 * 最大文件限制
	 */
	private int maxFileSize = FILE_SIZE_MAX;
	
	/**
	 * 最大内存使用量(单个上传文件)
	 */
	private int maxMemSize = MEM_SIZE_MAX;
	
	public void init() {
		
		filePath = getServletContext().getInitParameter("filePath");
		String pTmpPath = getServletContext().getInitParameter("tmpPath");
		String fSize = getServletContext().getInitParameter("maxFileSize");
		String mSize = getServletContext().getInitParameter("maxMemSize");
		errorUrl = getServletContext().getInitParameter("errorUrl");
		
		if (StringUtils.isNotEmpty(pTmpPath)) {
			this.tmpPath = pTmpPath;
		}
		File file = new File(tmpPath);
		if (!file.exists()) {
			file.mkdirs();
		}
		
		//自定义最大文件大小
		if (!StringUtils.isEmpty(fSize)) {
			this.maxFileSize = NumberUtils.toInt(fSize);
		}
		//自定义最大内存使用(文件小于这个大小将使用内存而不使用文件)
		if (!StringUtils.isEmpty(mSize)) {
			this.maxMemSize = NumberUtils.toInt(mSize);
		}
		//错误页面
		if (StringUtils.isBlank(errorUrl)) {
			//this.errorUrl = "/500.jsp";
		}
	}

	/**
	 * 不适用Get方法
	 */
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, java.io.IOException {
		throw new ServletException("GET method used with " + getClass().getName() + ": POST method required.");
	}

	@SuppressWarnings("unchecked")
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException {
		// Check that we have a file upload request
		isMultipart = ServletFileUpload.isMultipartContent(request);
		
		response.setContentType("text/html");
		java.io.PrintWriter out = response.getWriter();
		if( !isMultipart ){
			if (StringUtils.isBlank(errorUrl)) {
				out.println("<html>");
				out.println("<head>");
				out.println("<title>Servlet upload</title>"); 
				out.println("</head>");
				out.println("<body>");
				out.println("<p>No file uploaded</p>"); 
				out.println("</body>");
				out.println("</html>");
			} else {
				request.getRequestDispatcher(errorUrl);
			}
			return;
		}
		
		//上传权限判断
		if (!hasPermission(request)) {
			throw new AccessDeniedException("");
		}
		
		DiskFileItemFactory factory = new DiskFileItemFactory();
		
		// 将要存储在内存的最大值
		factory.setSizeThreshold(maxMemSize);
		
		// 存储在本地的最大空间分配(相比内存将分配更大空间)
		File tmpFile = new File(tmpPath);
		factory.setRepository(tmpFile);
		
		// 创建一个上传的新文件句柄
		ServletFileUpload upload = new ServletFileUpload(factory);
		
		// 上传文件的最大值
		upload.setSizeMax(maxFileSize);
		
		// 分解request，得到文件items.
		List<FileItem> fileItems;
		
		try {
			fileItems = upload.parseRequest(request);
			// Process the uploaded file items
			Iterator<FileItem> i = fileItems.iterator();

			int index = 0;
			Map<String, Object> fileMap = new HashMap<String, Object>();
			
			List<Object> fileObjList = new ArrayList<Object>();
			List<String> fileNameList = new ArrayList<String>();
			while(i.hasNext()) {
				FileItem item = i.next();
				if (!item.isFormField()) {
					//~~ 取得上传文件参数 =====================================================================
		            String fileName = item.getName();
		            Object obj = writer(item, request, index);
		            
		            fileObjList.add(obj);
		            fileNameList.add(fileName);
		            
		            index ++;
				}
			}
			
			target = request.getParameter("target");
			
			fileMap.put("name", fileNameList);
			fileMap.put("id", fileObjList);
			
			//do after
			after(request,response, fileMap);
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			//out.flush();
			//out.close();
		}
	}
	
	/**
	 * 文件写入方法
	 * @param index 索引
	 * @param item FileItem
	 * @param params 参数Map
	 * @throws Exception 异常
	 */
	public abstract Object writer(FileItem item, HttpServletRequest request, int index) throws Exception;

	protected void after(HttpServletRequest request, HttpServletResponse response, Map<String, Object> params) throws IOException {

		//OBJECT-TO-JSON	@see org.codehaus.jackson.map.jackson
		ObjectMapper objectMapper = new ObjectMapper();
		StringWriter writer = new StringWriter();
		//String json = objectMapper.writeValueAsString(fileMap);
		objectMapper.writeValue(writer, params);
		
		if (StringUtils.isNotBlank(target)) {
			
			response.sendRedirect(target + "?json=" + writer.toString());
		} else {
			//StringBuffer sb = new StringBuffer();
			//List<Object> fileNameList = (List<Object>) params.get("name");
			/*
			sb.append("<html>");
			sb.append("<head>");
			sb.append("<title>Servlet upload</title>"); 
			sb.append("</head>");
			sb.append("<body>");
			for(Object name : fileNameList) {
				 sb.append("Uploaded Filename: " + name + "<br>");
			}
			sb.append("</body>");
			sb.append("</html>");
			*/
			//sb.append(writer.toString());
			response.getWriter().write(writer.toString());
		}
	}
	/**
	 * 是否有上传权限
	 * @param request HttpServletRequest
	 * @return boolean
	 */
	protected boolean hasPermission(HttpServletRequest request) {
		
		return true;
	}
	
	public String getFilePath() {
		return filePath;
	}

	public String getTmpPath() {
		return tmpPath;
	}

	public int getMaxFileSize() {
		return maxFileSize;
	}

	public int getMaxMemSize() {
		return maxMemSize;
	}
}
